package com.cizzy.user.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cizzy.user.common.Result;
import com.cizzy.user.config.CizzyFinal;
import com.cizzy.user.entity.UserCount;
import com.cizzy.user.entity.dto.UserDTO;
import com.cizzy.user.entity.User;
import com.cizzy.user.entity.dto.UserHomeDTO;
import com.cizzy.user.entity.dto.UserInfoDTO;
import com.cizzy.user.entity.dto.VideoDTO;
import com.cizzy.user.feign.VideoFeign;
import com.cizzy.user.mapper.UserCountMapper;
import com.cizzy.user.mapper.UserMapper;
import com.cizzy.user.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.sql.Timestamp;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.cizzy.user.config.CizzyFinal.USER_LOCK;


/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author crissy-月下闲人
 * @since 2023-10-29
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Resource
    private UserMapper userMapper;
    @Resource
    private UserCountMapper userCountMapper;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private VideoFeign videoFeign;

    @Resource
    private RedissonClient redissonClient;

    private String userLock = USER_LOCK;


    @Override
    public Result<UserHomeDTO> getUserHomeById(Long id) {
        UserHomeDTO userHome = new UserHomeDTO();
        User user = userMapper.selectById(id);
        if (user == null) {
            return Result.error("查询用户失败");
        }

        UserCount userCount = userCountMapper.selectOne(new QueryWrapper<UserCount>().eq("user_id", id));
        if (userCount == null) {
            return Result.error("查询用户统计信息失败");
        }

        List<VideoDTO> videoDTOList = videoFeign.getVideoListByUserId(id).getData();
        if (videoDTOList == null) {
            return Result.error("查询用户视频信息失败");
        }

        userHome.convertFromUser(user);
        userHome.convertFromUserCount(userCount);
        userHome.convertFromVideoList(videoDTOList);
        return Result.success(userHome);
    }

    @Override
    public Result<User> getUserById(Long id) {
        String key = CizzyFinal.CIZZY_USER + id;
        String json = stringRedisTemplate.opsForValue().get(key);
        User user = null;
        //TODO 如果 Redis 中没有数据，则查询数据库
        if (!StrUtil.isBlank(json)) {
            user = JSONUtil.toBean(json, User.class);
        } else {
            user = userMapper.selectById(id);
            //TODO 如果数据库中没有数据，则直接返回；反之则 Redis 进行缓存
            if (user == null) {
                return Result.fail(400, "用户不存在");
            }
            stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(user), CizzyFinal.TTL_MIN, TimeUnit.SECONDS);
        }
        return Result.success(user);
    }

    @Override
    public synchronized Result<UserInfoDTO> userRegister(User user) {
        // 判断是否符合规则

        // 判断是否已存在该用户
        RLock lock = redissonClient.getLock(userLock);
        lock.lock();
        try {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.eq("username", user.getUsername());
            if (userMapper.exists(wrapper)) {
                return Result.fail(400, "用户名已存在");
            }
            user.setCreateAt(new Timestamp(System.currentTimeMillis()));
            userMapper.insert(user);
        } catch (Exception e) {
//            System.out.println("出现异常" + e);
            return Result.fail(500, "注册失败");
        } finally {
            lock.unlock();
        }

        return userLogin(user.getUsername(), user.getPassword());
    }


    @Override
    /**
     * 已完善，不需要修改！@Crissy-月下闲人 2023/11/6
     */
    public Result<UserInfoDTO> userLogin(String username, String password)  {

        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("username", username).eq("password", password);
        User user = userMapper.selectOne(wrapper);
        if (user == null) {
            return Result.fail(400, "用户名或密码错误");
        }

        //TODO 补充用户的相关信息
        String token = RandomUtil.randomString(CizzyFinal.TTL_TOKEN_LEN);
        user.setToken(token);
        UserCount userCount = userCountMapper.selectById(user.getUserId());
        UserInfoDTO userInfo = new UserInfoDTO(user, userCount);

        //TODO 用户相关数据（全）
        stringRedisTemplate.opsForValue().set(CizzyFinal.CIZZY_TOKEN + token, JSONUtil.toJsonPrettyStr(userInfo), CizzyFinal.TTL_TOKEN, TimeUnit.SECONDS);

        //TODO 单独存储用户ID
        stringRedisTemplate.opsForValue().set(CizzyFinal.CIZZY_USER_GETID + token, user.getUserId().toString(), CizzyFinal.TTL_TOKEN, TimeUnit.SECONDS);

        //TODO 脱敏
        user.lessMessage();

        return Result.success(200, "登录成功", userInfo);
    }

    @Override
    public Result<String> userLogout(String token) {
        stringRedisTemplate.delete(CizzyFinal.CIZZY_TOKEN + token);
        stringRedisTemplate.delete(CizzyFinal.CIZZY_USER_GETID + token);

        return Result.success(200, "退出登录成功");
    }

    @Override
    public synchronized Result<String> deleteUser(String token) {
        String key = CizzyFinal.CIZZY_TOKEN + token;
        String userStr = stringRedisTemplate.opsForValue().get(key);
        if (userStr == null) {
            return Result.fail(403, "用户未登录");
        }

        User user = JSONUtil.toBean(userStr, User.class);

        RLock lock = redissonClient.getLock(userLock);
        lock.lock();
        try {
            userMapper.delete(new QueryWrapper<User>().eq("username", user.getUsername()));
            stringRedisTemplate.delete(key);
        } catch (Exception e) {
            return Result.fail(500, "注销失败");
        } finally {
            lock.unlock();
        }
        return Result.success(200, "注销成功");
    }

    @Override
    public Result<String> initUser(String token) {
        String key = CizzyFinal.CIZZY_TOKEN + token;
        Boolean hasKey = stringRedisTemplate.hasKey(key);
        if (hasKey == null || !hasKey) {
            return Result.fail(403, "用户未登录");
        }
        return Result.success();
    }

    /**
     * 通过ID 批量查询
     *
     * @param userIdList
     * @return
     */
    @Override
    public Result<List<UserDTO>> getUserListByIds(List<Long> userIdList) {
//        List<User> userList = userMapper.selectBatchIds(userIdList);
        List<UserDTO> userDTOList = userIdList.stream().map(userId -> {
            UserDTO userDTO = new UserDTO();
            User user = userMapper.selectById(userId);
            BeanUtils.copyProperties(user, userDTO);
            return userDTO;
        }).collect(Collectors.toList());
        return Result.success(userDTOList);
    }

    @Override
    public Result<UserInfoDTO> getUserInfoById(Long userId) {
        if (userId == null) {
            return Result.error("参数错误");
        }
        User user = userMapper.selectById(userId);
        UserCount userCount = userCountMapper.selectById(userId);
        if (user == null || userCount == null) {
            return Result.error("查询失败");
        }
        UserInfoDTO userInfo = new UserInfoDTO();
        userInfo.convertFromUser(user);
        userInfo.convertFromUserCount(userCount);
        userInfo.setCollectCount((long) (0.414256 * userInfo.getFavoriteCount()));
        return Result.success(userInfo);
    }

    @Override
    public Result<List<Long>> getRandomUserIdList(Integer num) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByAsc("RAND()").
                last(String.format("LIMIT %d", num));
        List<User> userList = userMapper.selectList(queryWrapper);
        if(CollectionUtil.isEmpty(userList)) {
            return Result.success("查询到空用户列表", null);
        }
        return Result.success(userList.stream().map(User::getUserId).collect(Collectors.toList()));
    }
}
